import tkinter as tk
from tkinter import ttk, filedialog, messagebox, scrolledtext
import socket
import threading
import os
import base64
from PIL import Image, ImageTk
import time
import sys

class HackerChat:
    def __init__(self, root):
        self.root = root
        self.root.title("暗影通信 v1.0")
        self.root.geometry("800x600")
        self.root.configure(bg='black')
        
        # 设置黑客风格主题
        self.style = ttk.Style()
        self.style.theme_use('clam')
        self.style.configure('Hacker.TButton', 
                            background='black', 
                            foreground='#00ff00',
                            borderwidth=1,
                            focuscolor='none')
        self.style.map('Hacker.TButton', 
                      background=[('active', '#003300')])
        self.style.configure('Hacker.TEntry',
                            fieldbackground='black',
                            foreground='#00ff00',
                            borderwidth=1)
        self.style.configure('Hacker.TFrame',
                            background='black')
        self.style.configure('Hacker.TLabel',
                            background='black',
                            foreground='#00ff00')
        
        # 通信变量
        self.host = self.get_local_ip()
        self.port = 12345
        self.connected = False
        self.client_socket = None
        self.server_socket = None
        
        self.setup_ui()
        self.start_server()
        
    def get_local_ip(self):
        """获取本地IP地址"""
        try:
            s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            s.connect(("8.8.8.8", 80))
            ip = s.getsockname()[0]
            s.close()
            return ip
        except:
            return "127.0.0.1"
        
    def setup_ui(self):
        # 主框架
        main_frame = ttk.Frame(self.root, style='Hacker.TFrame')
        main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        
        # 标题
        title_label = ttk.Label(main_frame, text="暗影通信", style='Hacker.TLabel', 
                               font=('Courier', 20, 'bold'))
        title_label.pack(pady=(0, 10))
        
        # 连接状态
        status_frame = ttk.Frame(main_frame, style='Hacker.TFrame')
        status_frame.pack(fill=tk.X, pady=5)
        
        self.status_label = ttk.Label(status_frame, text="状态: 等待连接...", style='Hacker.TLabel')
        self.status_label.pack(side=tk.LEFT)
        
        # 连接信息
        self.connection_info = ttk.Label(status_frame, text=f"你的ID: {self.host}", style='Hacker.TLabel')
        self.connection_info.pack(side=tk.RIGHT)
        
        # 连接设置框架
        connection_frame = ttk.Frame(main_frame, style='Hacker.TFrame')
        connection_frame.pack(fill=tk.X, pady=10)
        
        ttk.Label(connection_frame, text="目标ID:", style='Hacker.TLabel').grid(row=0, column=0, padx=5, pady=5)
        self.target_ip_entry = ttk.Entry(connection_frame, style='Hacker.TEntry', width=20)
        self.target_ip_entry.grid(row=0, column=1, padx=5, pady=5)
        
        ttk.Label(connection_frame, text="端口:", style='Hacker.TLabel').grid(row=0, column=2, padx=5, pady=5)
        self.port_entry = ttk.Entry(connection_frame, style='Hacker.TEntry', width=10)
        self.port_entry.insert(0, str(self.port))
        self.port_entry.grid(row=0, column=3, padx=5, pady=5)
        
        self.connect_button = ttk.Button(connection_frame, text="连接", style='Hacker.TButton', 
                                        command=self.connect_to_target)
        self.connect_button.grid(row=0, column=4, padx=10, pady=5)
        
        self.disconnect_button = ttk.Button(connection_frame, text="断开", style='Hacker.TButton', 
                                           command=self.disconnect, state=tk.DISABLED)
        self.disconnect_button.grid(row=0, column=5, padx=5, pady=5)
        
        # 聊天区域
        chat_frame = ttk.Frame(main_frame, style='Hacker.TFrame')
        chat_frame.pack(fill=tk.BOTH, expand=True, pady=10)
        
        # 消息显示区域
        self.chat_display = scrolledtext.ScrolledText(chat_frame, height=15, width=70, 
                                                     bg='black', fg='#00ff00', 
                                                     insertbackground='#00ff00',
                                                     font=('Courier', 10))
        self.chat_display.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        self.chat_display.config(state=tk.DISABLED)
        
        # 输入区域
        input_frame = ttk.Frame(chat_frame, style='Hacker.TFrame')
        input_frame.pack(fill=tk.X, pady=5)
        
        self.message_entry = ttk.Entry(input_frame, style='Hacker.TEntry')
        self.message_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5)
        self.message_entry.bind('<Return>', self.send_message)
        
        self.send_button = ttk.Button(input_frame, text="发送", style='Hacker.TButton', 
                                     command=self.send_message, state=tk.DISABLED)
        self.send_button.pack(side=tk.RIGHT, padx=5)
        
        # 文件传输按钮
        file_frame = ttk.Frame(main_frame, style='Hacker.TFrame')
        file_frame.pack(fill=tk.X, pady=5)
        
        self.send_file_button = ttk.Button(file_frame, text="发送文件", style='Hacker.TButton',
                                          command=self.send_file, state=tk.DISABLED)
        self.send_file_button.pack(side=tk.LEFT, padx=5)
        
        self.send_image_button = ttk.Button(file_frame, text="发送图片", style='Hacker.TButton',
                                           command=self.send_image, state=tk.DISABLED)
        self.send_image_button.pack(side=tk.LEFT, padx=5)
        
        # 添加一些黑客风格的装饰文本
        self.add_hacker_decoration()
        
    def add_hacker_decoration(self):
        """添加黑客风格的装饰文本"""
        decorations = [
            "> 系统初始化完成...",
            "> 加密通道建立中...",
            "> 准备进行安全通信...",
            "> 输入 'help' 查看可用命令"
        ]
        
        for decoration in decorations:
            self.add_message("系统", decoration)
            time.sleep(0.5)
            self.root.update()
    
    def start_server(self):
        """启动服务器监听连接"""
        try:
            self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            self.server_socket.bind((self.host, self.port))
            self.server_socket.listen(1)
            
            # 在单独的线程中接受连接
            server_thread = threading.Thread(target=self.accept_connections)
            server_thread.daemon = True
            server_thread.start()
            
            self.add_message("系统", f"> 服务器已启动在 {self.host}:{self.port}")
        except Exception as e:
            self.add_message("系统", f"> 启动服务器失败: {str(e)}")
    
    def accept_connections(self):
        """接受传入的连接"""
        while True:
            try:
                client_socket, client_address = self.server_socket.accept()
                self.client_socket = client_socket
                self.connected = True
                
                # 更新UI状态
                self.root.after(0, self.update_connection_status, True, f"已连接: {client_address[0]}")
                
                # 启动接收消息的线程
                receive_thread = threading.Thread(target=self.receive_messages)
                receive_thread.daemon = True
                receive_thread.start()
                
                self.add_message("系统", f"> 已连接到 {client_address[0]}")
            except Exception as e:
                if self.server_socket:
                    self.add_message("系统", f"> 连接错误: {str(e)}")
    
    def connect_to_target(self):
        """连接到目标计算机"""
        target_ip = self.target_ip_entry.get()
        port = int(self.port_entry.get())
        
        if not target_ip:
            messagebox.showerror("错误", "请输入目标ID")
            return
        
        try:
            self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.client_socket.connect((target_ip, port))
            self.connected = True
            
            # 更新UI状态
            self.root.after(0, self.update_connection_status, True, f"已连接: {target_ip}")
            
            # 启动接收消息的线程
            receive_thread = threading.Thread(target=self.receive_messages)
            receive_thread.daemon = True
            receive_thread.start()
            
            self.add_message("系统", f"> 已连接到 {target_ip}")
        except Exception as e:
            self.add_message("系统", f"> 连接失败: {str(e)}")
            messagebox.showerror("连接错误", f"无法连接到 {target_ip}:{port}\n错误: {str(e)}")
    
    def disconnect(self):
        """断开连接"""
        if self.client_socket:
            self.client_socket.close()
            self.client_socket = None
        
        self.connected = False
        self.root.after(0, self.update_connection_status, False, "等待连接...")
        self.add_message("系统", "> 连接已断开")
    
    def update_connection_status(self, connected, status_text):
        """更新连接状态UI"""
        self.connected = connected
        self.status_label.config(text=f"状态: {status_text}")
        
        if connected:
            self.connect_button.config(state=tk.DISABLED)
            self.disconnect_button.config(state=tk.NORMAL)
            self.send_button.config(state=tk.NORMAL)
            self.send_file_button.config(state=tk.NORMAL)
            self.send_image_button.config(state=tk.NORMAL)
        else:
            self.connect_button.config(state=tk.NORMAL)
            self.disconnect_button.config(state=tk.DISABLED)
            self.send_button.config(state=tk.DISABLED)
            self.send_file_button.config(state=tk.DISABLED)
            self.send_image_button.config(state=tk.DISABLED)
    
    def send_message(self, event=None):
        """发送文本消息"""
        if not self.connected:
            messagebox.showwarning("警告", "未连接到任何客户端")
            return
        
        message = self.message_entry.get()
        if not message:
            return
        
        try:
            # 特殊命令处理
            if message.lower() == 'help':
                help_text = """
可用命令:
- clear: 清空聊天记录
- time: 显示当前时间
- status: 显示连接状态
                """
                self.add_message("系统", help_text)
                self.message_entry.delete(0, tk.END)
                return
            elif message.lower() == 'clear':
                self.chat_display.config(state=tk.NORMAL)
                self.chat_display.delete(1.0, tk.END)
                self.chat_display.config(state=tk.DISABLED)
                self.message_entry.delete(0, tk.END)
                return
            elif message.lower() == 'time':
                current_time = time.strftime("%Y-%m-%d %H:%M:%S")
                self.add_message("系统", f"当前时间: {current_time}")
                self.message_entry.delete(0, tk.END)
                return
            elif message.lower() == 'status':
                status = "已连接" if self.connected else "未连接"
                self.add_message("系统", f"连接状态: {status}")
                self.message_entry.delete(0, tk.END)
                return
            
            # 发送普通消息
            full_message = f"你: {message}"
            self.add_message("你", message)
            self.client_socket.send(f"MSG:{message}".encode())
            self.message_entry.delete(0, tk.END)
        except Exception as e:
            self.add_message("系统", f"> 发送消息失败: {str(e)}")
            self.disconnect()
    
    def receive_messages(self):
        """接收消息"""
        while self.connected:
            try:
                data = self.client_socket.recv(1024*1024)  # 1MB缓冲区
                if not data:
                    break
                
                message = data.decode()
                
                if message.startswith("MSG:"):
                    # 文本消息
                    text = message[4:]
                    self.root.after(0, self.add_message, "对方", text)
                elif message.startswith("FILE:"):
                    # 文件传输
                    file_info = message[5:].split(":", 1)
                    filename = file_info[0]
                    file_data = base64.b64decode(file_info[1])
                    
                    # 保存文件
                    save_path = filedialog.asksaveasfilename(
                        initialfile=filename,
                        title="保存文件"
                    )
                    
                    if save_path:
                        with open(save_path, 'wb') as f:
                            f.write(file_data)
                        self.root.after(0, self.add_message, "系统", f"> 文件已保存: {save_path}")
                elif message.startswith("IMAGE:"):
                    # 图片传输
                    image_info = message[6:].split(":", 1)
                    filename = image_info[0]
                    image_data = base64.b64decode(image_info[1])
                    
                    # 显示图片
                    self.root.after(0, self.show_image, image_data, filename)
            except Exception as e:
                if self.connected:  # 只在连接时显示错误
                    self.root.after(0, self.add_message, "系统", f"> 接收消息错误: {str(e)}")
                break
        
        # 连接断开
        if self.connected:
            self.root.after(0, self.disconnect)
    
    def add_message(self, sender, message):
        """添加消息到聊天显示区域"""
        self.chat_display.config(state=tk.NORMAL)
        
        # 添加发送者标签
        if sender == "你":
            self.chat_display.insert(tk.END, f"{sender}: ", "you")
        elif sender == "对方":
            self.chat_display.insert(tk.END, f"{sender}: ", "other")
        else:  # 系统消息
            self.chat_display.insert(tk.END, f"{sender}: ", "system")
        
        # 添加消息内容
        self.chat_display.insert(tk.END, f"{message}\n")
        
        self.chat_display.config(state=tk.DISABLED)
        self.chat_display.see(tk.END)
    
    def send_file(self):
        """发送文件"""
        if not self.connected:
            messagebox.showwarning("警告", "未连接到任何客户端")
            return
        
        file_path = filedialog.askopenfilename(
            title="选择要发送的文件"
        )
        
        if not file_path:
            return
        
        try:
            with open(file_path, 'rb') as f:
                file_data = f.read()
            
            filename = os.path.basename(file_path)
            encoded_data = base64.b64encode(file_data).decode()
            
            self.client_socket.send(f"FILE:{filename}:{encoded_data}".encode())
            self.add_message("系统", f"> 文件已发送: {filename}")
        except Exception as e:
            self.add_message("系统", f"> 发送文件失败: {str(e)}")
    
    def send_image(self):
        """发送图片"""
        if not self.connected:
            messagebox.showwarning("警告", "未连接到任何客户端")
            return
        
        image_path = filedialog.askopenfilename(
            title="选择要发送的图片",
            filetypes=[("图片文件", "*.png;*.jpg;*.jpeg;*.gif;*.bmp")]
        )
        
        if not image_path:
            return
        
        try:
            with open(image_path, 'rb') as f:
                image_data = f.read()
            
            filename = os.path.basename(image_path)
            encoded_data = base64.b64encode(image_data).decode()
            
            self.client_socket.send(f"IMAGE:{filename}:{encoded_data}".encode())
            self.add_message("系统", f"> 图片已发送: {filename}")
        except Exception as e:
            self.add_message("系统", f"> 发送图片失败: {str(e)}")
    
    def show_image(self, image_data, filename):
        """显示接收到的图片"""
        try:
            # 创建新窗口显示图片
            image_window = tk.Toplevel(self.root)
            image_window.title(f"接收的图片: {filename}")
            image_window.geometry("400x400")
            image_window.configure(bg='black')
            
            # 加载图片
            image = Image.open(io.BytesIO(image_data))
            image.thumbnail((380, 380))  # 调整图片大小以适应窗口
            photo = ImageTk.PhotoImage(image)
            
            # 显示图片
            label = ttk.Label(image_window, image=photo, style='Hacker.TLabel')
            label.image = photo  # 保持引用
            label.pack(padx=10, pady=10)
            
            # 添加文件名标签
            filename_label = ttk.Label(image_window, text=filename, style='Hacker.TLabel')
            filename_label.pack(pady=5)
            
            self.add_message("系统", f"> 已接收图片: {filename}")
        except Exception as e:
            self.add_message("系统", f"> 显示图片失败: {str(e)}")

# 添加必要的导入
import io

def main():
    # 检查必要的库是否已安装
    try:
        from PIL import Image, ImageTk
    except ImportError:
        print("缺少必要的库，正在安装...")
        import subprocess
        import sys
        subprocess.check_call([sys.executable, "-m", "pip", "install", "pillow"])
        from PIL import Image, ImageTk
    
    root = tk.Tk()
    app = HackerChat(root)
    root.mainloop()

if __name__ == "__main__":
    main()